package com.gitee.wsl.mathematics.matrix

import com.gitee.wsl.io.dataproxy.DataProxyContainer
import com.gitee.wsl.mathematics.number.DoubleCalcContainer
import com.gitee.wsl.mathematics.number.FloatCalcContainer
import com.gitee.wsl.mathematics.vector.MutableVector
import com.gitee.wsl.mathematics.vector.Vector

interface MutableMatrix<T:Number,V:Matrix<T,V,R>,R:Vector<T,R>>:Matrix<T,V,R>, DataProxyContainer<T> {

    fun set(other:V) = proxy.updateIndexed { index, _ -> other.proxy[index]}//other.valueArray.forEachIndexed { index, t -> proxy[index] = t }

    fun clear() = proxy.fill(zero)

    fun negate() = proxy.update { -it }

    override val m: List<MutableVector<T,R>>

    override val n: List<MutableVector<T,R>>

    fun abs()   = proxy.update { abs(it) }
    fun sign()  = proxy.update { sign(it) }
    fun ceil()  = proxy.update { ceil(it) }
    fun floor() = proxy.update { floor(it) }
    fun round() = proxy.update { round(it) }

    fun adjoin() = set(adjoin)

    fun inverse() = set(inverse)

    fun scale(factor: T) =  proxy.update { it * factor }

    fun copy(block: MatCopyInitializer<T>):MutableMatrix<T,V,R>

    fun copySelf() = copy(block = {i, j -> get(i,j)} )

    fun copyEmpty() = copy(block ={ _, _ -> zero})

    fun copyIdentity() = copy(block ={ i, j -> if( i == j) one else zero})

    /*fun setValue(rowIndex:Int,colIndex:Int,value:T) {
        proxy[rowIndex col colIndex] = value
    }*/

    //override fun createVec(indexRage:IntRange) = createVec(indexRage.toList())

    //fun update(block:()->Unit) = block()

    override fun createVec(indexes:List<Int>):MutableVector<T,R>

    infix  fun  Pair<Int,Int>.set(value: T) = set(this.first,this.second,value)

    infix  fun  Pair<Int,Int>.swap(other: Pair<Int,Int>){
        val d = get(this.first,this.second)
        set(get(other.first,other.second))
        other.set(d)
    }

    operator fun timesAssign(v: Number) = proxy.update {  t -> t * v }

    operator fun divAssign(v: Number) = proxy.update {  t -> t / v }

    //operator fun plusAssign(other: V) = proxy.updateIndexed { index, t -> t + other.proxy[index] }

    fun subtract(other: V) = proxy.updateIndexed { index, t -> t - other.proxy[index] }

    operator fun divAssign(other: V) = set(div(other))

    //operator fun timesAssign(other: V)

    override operator fun get(row: Int) = createVec(rowIndexes(row))

    /*operator fun set(row: Int,value: R){
        this[row].set(value)
        //this[row].proxy.updateIndexed { index, _ -> value[index]  }
    }*/

    operator fun set(row: Int, col: Int,value:T) {
        proxy[row col col] = value
    }

    override operator fun M.get(row: Int) = createVec(rowIndexes(row))

    override operator fun N.get(col: Int) = createVec(colIndexes(col))

    fun rowUpdate(rowIndex:Int,block:(colIndex:Int,T)->T){
        (0..colCount).forEach {
            val value = this[rowIndex,it]
            this[rowIndex,it] = block(it,value)
        }
    }

    fun columnUpdate(colIndex:Int,block:(rowIndex:Int,T)->T){
        (0..rowCount).forEach {
            val value = this[it,colIndex]
            this[it,colIndex] = block(it,value)
        }
    }

    /**
     * Elementary transformation: swap the column [c0] with column [c1]
     * 初等列变换：[c0]和[c1]交换
     */
    fun exchangeColumn(c0: Int, c1: Int){
        (0..rowCount).forEach {
            (it to c0) swap (it to c1)
        }
    }

    fun exchangeRow(r0: Int, r1: Int){
        (0..colCount).forEach {
            (r0 to it) swap (r1 to it)
        }
    }

    /**
     * Elementary transformation: multiply the row [r] with [k]
     * 初等行变换：第 [r] 行数乘 [k]
     */
    fun timesRow(r: Int, k: T) = rowUpdate(r){_,value-> value*k }

    /**
     * Elementary transformation: multiply the row [r0] with [k], and add it into row [r1]
     * 初等行变换：[k] 倍 [r0] 加到 [r1]
     */
    fun plusToRow(k: T, r0: Int, r1: Int)= rowUpdate(r1){colIndex,value-> value + this[r0,colIndex]*k }

    /**
     * Produces a matrix with this context and given dimensions.
     */
    fun update(initializer: MatInitializer<T>) = buildMatrix(this,initializer)

    /**
     * Produces a matrix with this context and given dimensions.
     */
    fun<Q:MutableMatrix<T,*,*>> buildMatrix(mutableMatrix:Q , initializer: MatInitializer<T>):Q {
        (0 until rowCount).forEach {i->
            (0 until colCount).forEach { j->
                mutableMatrix[i, j] = initializer(i,j)
            }
        }
        return mutableMatrix
    }


}

interface MutableMatrixD<V:Matrix<Double,V,R>,R:Vector<Double,R>>:MutableMatrix<Double,V,R>, DoubleCalcContainer

interface MutableMatrixF<V:Matrix<Float,V,R>,R:Vector<Float,R>>:MutableMatrix<Float,V,R>, FloatCalcContainer
